/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright 2009 - 2012 Luca Mingardi.
 *
 * This file is part of jeeObserver.
 *
 * JeeObserver is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * JeeObserver is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */
package jeeobserver.server;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import jeeobserver.ActionObserver;
import jeeobserver.HardDiskObserver;
import jeeobserver.HttpSessionObserver;
import jeeobserver.JvmObserver;
import jeeobserver.Observer;
import jeeobserver.ObserverBulk;
import jeeobserver.server.settings.ActionExclusionRule;
import jeeobserver.server.settings.ActionNotificationRule;
import jeeobserver.server.settings.HardDiskExclusionRule;
import jeeobserver.server.settings.HardDiskNotificationRule;
import jeeobserver.server.settings.HttpSessionExclusionRule;
import jeeobserver.server.settings.HttpSessionNotificationRule;
import jeeobserver.server.settings.JvmExclusionRule;
import jeeobserver.server.settings.JvmNotificationRule;
import jeeobserver.server.settings.Settings;

/**
 * The Class JeeObserverServer.
 *
 * @author Luca Mingardi
 * @version 4.0
 */
final class JeeObserverServer extends Thread {

	 private ServerSocket server;

	 private boolean enabled;

	 JeeObserverServer(int port) throws JeeObserverServerException {

			try {
				 this.server = new ServerSocket(port);
			} catch (final Exception e) {
				 throw new JeeObserverServerException("Error starting server.", e);
			}

			this.enabled = true;

			this.setDaemon(false);
	 }

	 @Override
	 public void run() {

			JeeObserverServerContext.logger.log(Level.FINE, "Server started on port: {0}", String.valueOf(this.server.getLocalPort()));

			while (this.enabled) {
				 try {
						final Socket client = this.server.accept();
						if (!this.enabled) {
							 break;
						}
						final RequestProcessor requestProcessor = new RequestProcessor(client);
						requestProcessor.start();

				 } catch (final Exception e) {
						JeeObserverServerContext.logger.log(Level.SEVERE, e.getMessage(), e);
				 }
			}
	 }

	 private class RequestProcessor extends Thread {

			/** The client. */
			private Socket client = null;

			/** The object input stream. */
			private ObjectInputStream objectInputStream = null;

			/** The object output stream. */
			private ObjectOutputStream objectOutputStream = null;

			/**
			 * Instantiates a new request processor.
			 *
			 * @param socket the socket
			 */
			RequestProcessor(Socket socket) {
				 this.client = socket;
				 try {
						this.objectInputStream = new ObjectInputStream(this.client.getInputStream());
						this.objectOutputStream = new ObjectOutputStream(this.client.getOutputStream());

				 } catch (final Exception e1) {
						try {
							 this.client.close();
						} catch (final Exception e) {
							 JeeObserverServerContext.logger.log(Level.SEVERE, e.getMessage(), e);
						}
						JeeObserverServerContext.logger.log(Level.SEVERE, e1.getMessage(), e1);
						return;
				 }
				 this.setDaemon(false);
			}

			@Override
			public void run() {
				 try {

						final Object parameter = this.objectInputStream.readObject();
						JeeObserverServerContext.logger.log(Level.FINER, "Host: {0} Request parameter: {1}", new Object[]{this.client.getInetAddress().getHostAddress(), parameter.getClass().getSimpleName()});

						Object result;

						try {
							 result = JeeObserverServer.dispatch(parameter);
						} catch (final JeeObserverServerException e) {
							 JeeObserverServerContext.logger.log(Level.SEVERE, e.getMessage(), e);
							 result = new JeeObserverServerException(e.getMessage());
						}

						// If client socket close it after request
						// if (parameter instanceof ClientRequest) {
						this.objectOutputStream.writeObject(result);
						this.objectOutputStream.flush();

						this.objectOutputStream.close();
						this.objectInputStream.close();

						this.client.close();


				 } catch (final Exception e) {
						JeeObserverServerContext.logger.log(Level.SEVERE, e.getMessage(), e);
				 }
			}
	 }

	 void close() throws JeeObserverServerException {

			this.enabled = false;

			try {
				 final Socket socket = new Socket(this.server.getInetAddress(), this.server.getLocalPort());
				 socket.close();

				 this.server.close();
			} catch (final Exception e) {
				 throw new JeeObserverServerException("Error stopping server.", e);
			}

			JeeObserverServerContext.logger.log(Level.FINE, "Server stopped");
	 }

	 static Object dispatch(Object parameter) throws JeeObserverServerException {

			// ObserverBulk
			if (parameter instanceof ObserverBulk) {

				 final long startTimeMills = System.currentTimeMillis();

				 // Load settings
				 final Settings settings = JeeObserverServerContext.getInstance().getDatabaseHandler().loadSettings();

				 final ObserverBulk<Observer> observerBulk = (ObserverBulk) parameter;

				 final List<ActionObserver> actionObserversList = new ArrayList<ActionObserver>();
				 final List<JvmObserver> jvmObserversList = new ArrayList<JvmObserver>();
				 final List<HardDiskObserver> hardDiskObserversList = new ArrayList<HardDiskObserver>();
				 final List<HttpSessionObserver> httpSessionObserversList = new ArrayList<HttpSessionObserver>();

				 // Scan all invocations
				 for (final Observer observer : observerBulk) {

						// Handle Action Invocation
						if (observer instanceof ActionObserver) {
							 ActionObserver actionObserver = (ActionObserver) observer;

							 // Remove if observer is not enabled
							 if ((actionObserver.getCategory() == ActionObserver.CATEGORY_SERVLET) && !settings.isServletObserverEnabled()) {
									actionObserver = null;
							 } else if ((actionObserver.getCategory() == ActionObserver.CATEGORY_JSF) && !settings.isJsfObserverEnabled()) {
									actionObserver = null;
							 } else if ((actionObserver.getCategory() == ActionObserver.CATEGORY_EJB) && !settings.isEjbObserverEnabled()) {
									actionObserver = null;
							 } else if ((actionObserver.getCategory() == ActionObserver.CATEGORY_CUSTOM) && !settings.isCustomObserverEnabled()) {
									actionObserver = null;
							 } else {
									// Check exclusions rules
									for (final ActionExclusionRule actionExclusionRule : settings.getActionExclusionRulesList()) {
										 if (actionExclusionRule.isObserverInRule(actionObserver)) {
												actionObserver = null;
												break;
										 }
									}

									// Check notifications
									if (actionObserver != null) {
										 for (final ActionNotificationRule actionNotificationRule : settings.getActionNotificationRulesList()) {
												if (actionNotificationRule.isObserverInRule(actionObserver)) {
													 // Send notification
													 if (actionNotificationRule.getNotification(actionObserver).send()) {
															//TODO
													 }
												}
										 }
									}
							 }

							 if (actionObserver != null) {
									actionObserversList.add(actionObserver);
							 }

						} else if (observer instanceof JvmObserver) {
							 JvmObserver jvmObserver = (JvmObserver) observer;

							 // Remove if observer is not enabled
							 if (!settings.isJvmObserverEnabled()) {
									jvmObserver = null;
							 } else {
									// Check exclusions rules
									for (final JvmExclusionRule jvmExclusionRule : settings.getJvmExclusionRulesList()) {
										 if (jvmExclusionRule.isObserverInRule(jvmObserver)) {
												jvmObserver = null;
												break;
										 }
									}

									// Check notifications
									if (jvmObserver != null) {
										 for (final JvmNotificationRule jvmNotificationRule : settings.getJvmNotificationRulesList()) {
												if (jvmNotificationRule.isObserverInRule(jvmObserver)) {
													 // Send notification
													 if (jvmNotificationRule.getNotification(jvmObserver).send()) {
															//TODO
													 }
												}
										 }
									}
							 }

							 if (jvmObserver != null) {
									jvmObserversList.add(jvmObserver);
							 }

						} else if (observer instanceof HardDiskObserver) {
							 HardDiskObserver hardDiskObserver = (HardDiskObserver) observer;

							 // Remove if observer is not enabled
							 if (!settings.isHardDiskObserverEnabled()) {
									hardDiskObserver = null;
							 } else {
									// Check exclusions rules
									for (final HardDiskExclusionRule hardDiskExclusionRule : settings.getHardDiskExclusionRulesList()) {
										 if (hardDiskExclusionRule.isObserverInRule(hardDiskObserver)) {
												hardDiskObserver = null;
												break;
										 }
									}

									// Check notifications
									if (hardDiskObserver != null) {
										 for (final HardDiskNotificationRule hardDiskNotificationRule : settings.getHardDiskNotificationRulesList()) {
												if (hardDiskNotificationRule.isObserverInRule(hardDiskObserver)) {
													 // Send notification
													 if (hardDiskNotificationRule.getNotification(hardDiskObserver).send()) {
															//TODO
													 }
												}
										 }
									}
							 }

							 if (hardDiskObserver != null) {
									hardDiskObserversList.add(hardDiskObserver);
							 }

						} else if (observer instanceof HttpSessionObserver) {
							 HttpSessionObserver httpSessionObserver = (HttpSessionObserver) observer;

							 // Remove if observer is not enabled
							 if (!settings.isHttpSessionObserverEnabled()) {
									httpSessionObserver = null;
							 } else {
									// Check exclusions rules
									for (final HttpSessionExclusionRule httpSessionExclusionRule : settings.getHttpSessionExclusionRulesList()) {
										 if (httpSessionExclusionRule.isObserverInRule(httpSessionObserver)) {
												httpSessionObserver = null;
												break;
										 }
									}

									// Check notifications
									if (httpSessionObserver != null) {
										 for (final HttpSessionNotificationRule httpSessionNotificationRule : settings.getHttpSessionNotificationRulesList()) {
												if (httpSessionNotificationRule.isObserverInRule(httpSessionObserver)) {
													 // Send notification
													 if (httpSessionNotificationRule.getNotification(httpSessionObserver).send()) {
															//TODO
													 }
												}
										 }
									}
							 }

							 if (httpSessionObserver != null) {
									httpSessionObserversList.add(httpSessionObserver);
							 }
						}
				 }

				 // Persist Http Session
				 JeeObserverServerContext.getInstance().getDatabaseHandler().persistHttpSessionStatistics(httpSessionObserversList);

				 // Persist JVM
				 JeeObserverServerContext.getInstance().getDatabaseHandler().persistJvmStatistics(jvmObserversList);

				 // Persist Hard disk
				 JeeObserverServerContext.getInstance().getDatabaseHandler().persistHardDiskStatistics(hardDiskObserversList);

				 // Persist Action
				 JeeObserverServerContext.getInstance().getDatabaseHandler().persistActionStatistics(actionObserversList);

				 final long stopTimeMills = System.currentTimeMillis();

				 JeeObserverServerContext.logger.log(Level.FINE, "Persisted all statistics. ({0} ms.)", new Object[]{String.valueOf(stopTimeMills - startTimeMills)});

				 return new Long(0);
			} // LoadSettingsParameter
			else if (parameter instanceof SettingsParameters.LoadParameter) {
				 return JeeObserverServerContext.getInstance().getDatabaseHandler().loadSettings();
			} // UpdateSettingsParameter
			else if (parameter instanceof SettingsParameters.UpdateParameter) {
				 JeeObserverServerContext.getInstance().getDatabaseHandler().updateSettings(((SettingsParameters.UpdateParameter) parameter).getSettings());
				 return null;
			} // SearchActionElementsParameter
			else if (parameter instanceof ActionParameters.SearchElementsParameter) {
				 final ActionParameters.SearchElementsParameter parameters = ((ActionParameters.SearchElementsParameter) parameter);
				 return JeeObserverServerContext.getInstance().getDatabaseHandler().searchActionElements(parameters);
			} // SearchActionStatisticsParameter
			else if (parameter instanceof ActionParameters.SearchStatisticsParameter) {
				 return JeeObserverServerContext.getInstance().getDatabaseHandler().searchActionStatistics((ActionParameters.SearchStatisticsParameter) parameter);
			} // SearchJvmElementsParameter
			else if (parameter instanceof JvmParameters.SearchElementsParameter) {
				 return JeeObserverServerContext.getInstance().getDatabaseHandler().searchJvmElements((JvmParameters.SearchElementsParameter) parameter);
			} // SearchJvmStatisticsParameter
			else if (parameter instanceof JvmParameters.SearchStatisticsParameter) {
				 return JeeObserverServerContext.getInstance().getDatabaseHandler().searchJvmStatistics((JvmParameters.SearchStatisticsParameter) parameter);
			}// SearchHttpSessionStatisticsParameter
			else if (parameter instanceof HttpSessionParameters.SearchStatisticsParameter) {
				 return JeeObserverServerContext.getInstance().getDatabaseHandler().searchHttpSessionStatistics((HttpSessionParameters.SearchStatisticsParameter) parameter);
			} // SearchHttpSessionElementsParameter
			else if (parameter instanceof HttpSessionParameters.SearchElementsParameter) {
				 return JeeObserverServerContext.getInstance().getDatabaseHandler().searchHttpSessionElements((HttpSessionParameters.SearchElementsParameter) parameter);
			}// SearchHardDiskStatisticsParameter
			else if (parameter instanceof HardDiskParameters.SearchStatisticsParameter) {
				 return JeeObserverServerContext.getInstance().getDatabaseHandler().searchHardDiskStatistics((HardDiskParameters.SearchStatisticsParameter) parameter);
			} // SearchHardDiskElementsParameter
			else if (parameter instanceof HardDiskParameters.SearchElementsParameter) {
				 return JeeObserverServerContext.getInstance().getDatabaseHandler().searchHardDiskElements((HardDiskParameters.SearchElementsParameter) parameter);
			} // DeleteActionsElementsParameter
			else if (parameter instanceof ActionParameters.DeleteStatisticsParameter) {
				 final ActionParameters.DeleteStatisticsParameter parameters = ((ActionParameters.DeleteStatisticsParameter) parameter);
				 JeeObserverServerContext.getInstance().getDatabaseHandler().deleteActionStatistics(parameters);
				 return null;
			} // DeleteJvmStatisticsParameter
			else if (parameter instanceof JvmParameters.DeleteStatisticsParameter) {
				 JeeObserverServerContext.getInstance().getDatabaseHandler().deleteJvmStatistics((JvmParameters.DeleteStatisticsParameter) parameter);
				 return null;
			} // DeleteHttpSessionStatisticsParameter
			else if (parameter instanceof HttpSessionParameters.DeleteStatisticsParameter) {
				 JeeObserverServerContext.getInstance().getDatabaseHandler().deleteHttpSessionStatistics((HttpSessionParameters.DeleteStatisticsParameter) parameter);
				 return null;
			} // DeleteHttpSessionStatisticsParameter
			else if (parameter instanceof HardDiskParameters.DeleteStatisticsParameter) {
				 JeeObserverServerContext.getInstance().getDatabaseHandler().deleteHardDiskStatistics((HardDiskParameters.DeleteStatisticsParameter) parameter);
				 return null;
			}

			throw new JeeObserverServerException("Object \"" + parameter.getClass().getCanonicalName() + "\" could not be dispatched");
	 }

	 boolean isEnabled() {
			return this.enabled;
	 }

	 void setEnabled(boolean enabled) {
			this.enabled = enabled;
	 }

	 ServerSocket getServer() {
			return this.server;
	 }
}
